<!DOCTYPE html>
<html>
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
	<title>Meta Balls</title>
	<link rel="stylesheet" href="../css/style.css">
	<script type="text/javascript" src="../../lib/paper.js"></script>
	<script type="text/paperscript" canvas="canvas">
		// Ported from original Metaball script by SATO Hiroyuki
		// http://park12.wakwak.com/~shp/lc/et/en_aics_script.html
		project.currentStyle = {
			fillColor: 'black'
		};

		var handle_len_rate = 2.4;
		var circlePaths = [];
		var circlePath;
		var radius = 50;
		circlePaths.push(new Path.Circle(view.center, 100));
		function onMouseDown(event) {
			circlePath = null;
			for (var i = 0, l = circlePaths.length; i < l; i++) {
				var path = circlePaths[i];
				if (path.position.getDistance(event.point) < 50)
					circlePath = path;
			}
			if (!circlePath) {
				circlePath = new Path.Circle(event.point, 50);
				circlePaths.push(circlePath);
			}
			generateConnections(circlePaths);
		}

		function onMouseDrag(event) {
			circlePath.position = event.point;
			generateConnections(circlePaths);
		}

		function generateConnections(paths) {
			for (var i = 0, l = paths.length; i < l; i++) {
				for (var j = i - 1; j >= 0; j--) {
					var path = metaball(paths[i], paths[j], 0.5, handle_len_rate, 300);
					if (path) {
						path.removeOn({
							drag: true,
							down: true
						});
					}
				}
			}
		}

		// ---------------------------------------------
		function metaball(ball1, ball2, v, handle_len_rate, maxDistance) {
			var center1 = ball1.position;
			var center2 = ball2.position;
			var radius1 = ball1.bounds.width / 2;
			var radius2 = ball2.bounds.width / 2;
			var pi2 = Math.PI / 2;
			var d = center1.getDistance(center2);
			var u1, u2;

			if (radius1 == 0 || radius2 == 0)
				return;

			if (d > maxDistance || d <= Math.abs(radius1 - radius2)) {
				return;
			} else if (d < radius1 + radius2) { // case circles are overlapping
				u1 = Math.acos((radius1 * radius1 + d * d - radius2 * radius2) / (2 * radius1 * d));
				u2 = Math.acos((radius2 * radius2 + d * d - radius1 * radius1) / (2 * radius2 * d));
			} else {
				u1 = 0;
				u2 = 0;
			}

			var angle1 = (center2 - center1).getAngleInRadians();
			var angle2 = Math.acos((radius1 - radius2) / d);
			var angle1a = angle1 + u1 + (angle2 - u1) * v;
			var angle1b = angle1 - u1 - (angle2 - u1) * v;
			var angle2a = angle1 + Math.PI - u2 - (Math.PI - u2 - angle2) * v;
			var angle2b = angle1 - Math.PI + u2 + (Math.PI - u2 - angle2) * v;
			var p1a = center1 + getVector(angle1a, radius1);
			var p1b = center1 + getVector(angle1b, radius1);
			var p2a = center2 + getVector(angle2a, radius2);
			var p2b = center2 + getVector(angle2b, radius2);
			// define handle length by the distance between both ends of the curve to draw
			var d2 = Math.min(v * handle_len_rate, p1a.getDistance(p2a) / (radius1 + radius2));

			d2 *= Math.min(1, d * 2 / (radius1 + radius2)); // case circles are overlapping
			radius1 *= d2;
			radius2 *= d2;

			var path = new Path([p1a, p2a, p2b, p1b]);
			path.style = ball1.style;
			path.closed = true;
			var segments = path.segments;
			segments[0].handleOut = getVector(angle1a - pi2, radius1);
			segments[1].handleIn = getVector(angle2a + pi2, radius2);
			segments[2].handleOut = getVector(angle2b - pi2, radius2);
			segments[3].handleIn = getVector(angle1b + pi2, radius1);
			return path;
		}

		// ------------------------------------------------
		function getVector(radians, length) {
			return new Point({
				// Convert radians to degrees:
				angle: radians * 180 / Math.PI,
				length: length
			});
		}
	</script>
</head>
<body>
	<canvas id="canvas" resize></canvas>
</body>
</html>